package org.gokeep.communication.engine.udp;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.MulticastSocket;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * 组播服务
 *
 * @author xuning
 */
@Slf4j
public class UdpServer implements Runnable {

    private InetAddress address;

    private int bufferSize = 4096;

    private boolean run = true;

    private List<MessageProcess> messageProcesses;

    private Consumer<DatagramPacket> process;

    private byte[] buffer;

    /**
     * 链式编程构建
     *
     * @return
     */
    public static UdpServer build() {
        return new UdpServer();
    }

    /**
     * 无参数构建
     */
    public UdpServer() {

    }

    /**
     * 设置地址
     *
     * @param host
     * @param port
     * @return
     */
    public UdpServer address(String host, int port) {
        this.address = InetAddress.build(host, port);
        return this;
    }

    /**
     * 设置地址
     *
     * @param address
     * @return
     */
    public UdpServer address(InetAddress address) {
        this.address = address;
        return this;
    }

    /**
     * 绑定地址
     *
     * @param host
     * @param port
     * @return
     */
    public UdpServer bind(String host, int port) {
        this.address = InetAddress.build(host, port);
        return this;
    }

    /**
     * 绑定地址
     *
     * @param address
     * @return
     */
    public UdpServer bind(InetAddress address) {
        this.address = address;
        return this;
    }


    /**
     * 实例化构建地址
     *
     * @param inetAddress
     */
    public UdpServer(InetAddress inetAddress) {
        this.address = inetAddress;
    }

    /**
     * 设置buffer大小
     *
     * @param bufferSize
     * @return
     */
    public UdpServer bufferSize(int bufferSize) {
        this.bufferSize = bufferSize;
        this.buffer = new byte[this.bufferSize];
        return this;
    }

    /**
     * 添加处理逻辑
     *
     * @param process
     * @return
     */
    public UdpServer process(Consumer<DatagramPacket> process) {
        this.process = process;
        return this;
    }

    /**
     * 添加处理器
     *
     * @param messageProcessClass
     * @return
     */
    @SneakyThrows
    public <T extends MessageProcess> UdpServer addProcess(Class<T> messageProcessClass) {
        if (this.messageProcesses == null) {
            this.messageProcesses = new ArrayList<>();
        }
        T process = messageProcessClass.newInstance();
        this.messageProcesses.add(process);
        return this;
    }

    /**
     * 停止服务
     */
    public void stop() {
        this.run = true;
    }

    /**
     * 启动监听服务
     * 启动UDP组播组
     */
    @Override
    public void run() {
        try (MulticastSocket multicastSocket = new MulticastSocket(2002)) {
            this.buffer = new byte[this.bufferSize];
            multicastSocket.joinGroup(this.address.socketAddress().getAddress());
            while (run) {
                DatagramPacket datagramPacket = new DatagramPacket(this.buffer, this.buffer.length);
                multicastSocket.receive(datagramPacket);
                this.messageProcesses.forEach(messageProcess -> messageProcess.execute(datagramPacket));
            }
        } catch (IOException e) {
            log.warn("Start udp server fail, cause: ", e);
        }
    }

    /**
     * 启动服务
     */
    public void start() {
        Thread thread = new Thread(this);
        thread.setDaemon(false);
        thread.start();
        log.info("Start udp server listen is success, host: {}, port: {}", this.address.getHost(), this.address.getPort());
    }
}
